#ifndef CSE168_RAY_H_INCLUDED
#define CSE168_RAY_H_INCLUDED

#include "Vector3.h"

#ifdef BETTER_RANDOM
//#include "mt-64.c"
#endif


class Ray
{
public:
    Vector3 o,      //!< Origin of ray
            d,      //!< Direction of ray
			inv_d;
	__m128 invd;

    Ray() : o(), d(Vector3(0.0f,0.0f,1.0f))
    {
        // empty
    }

    Ray(const Vector3& o, const Vector3& d, float w=-1) : o(o), d(d)
    {
		inv_d = Vector3(1/d.x, 1/d.y, 1/d.z);
		invd = _mm_loadu_ps(&inv_d.x);
		//invd = _mm_rcp_ps(_mm_loadu_ps(&d.x));
    }
};


class Rand
{
public:
	static float getRand()
	{
#ifdef BETTER_RANDOM
		return float(genrand64_real1());
#else
		return float(rand())/RAND_MAX;
#endif
	}

	static Vector3 getRandDir(Vector3 n, float u1=-10, float u2=-10)
	{
		if (u1 < -9)
			u1 = getRand();
		if (u2 < -9)
			u2 = getRand();

		float sin2 =  ((float) rand())/RAND_MAX;
		float sintheta;

		SSEsqrt(&sintheta, &sin2);
		float costheta = cos(asin(sintheta));
		float phi = 2*PI*(((float) rand())/RAND_MAX);

		float x = -sintheta*sin(phi);
		float y = sintheta*cos(phi);

		float cos2 = 1.0f - (sintheta * sintheta);
		float z;
		SSEsqrt(&z, &cos2);

		Vector3 tangentRay;
		if(fabs(n.x) > fabs(n.y))
			tangentRay = Vector3(0, 1, 0);
		else
			tangentRay = Vector3(1, 0, 0);
		tangentRay = cross(n, tangentRay);
		tangentRay.normalize();

		Vector3 randRay = x*tangentRay + y*cross(n, tangentRay) + z*n;

		return randRay;
	}
};

//! Contains information about a ray hit with a surface.
/*!
    HitInfos are used by object intersection routines. They are useful in
    order to return more than just the hit distance.
*/
class HitInfo
{
public:
    float t;                            //!< The hit distance
    Vector3 P;                          //!< The hit point
    Vector3 N;                          //!< Shading normal vector
    const Material* material;           //!< Material of the intersected object
	int extraHits;

    //! Default constructor.
    explicit HitInfo(float t = 0.0f,
                     const Vector3& P = Vector3(),
                     const Vector3& N = Vector3(0.0f, 1.0f, 0.0f)) :
        t(t), P(P), N(N), material (0)
    {
		extraHits = 0;
    }
};

// all below from Toshiya's PPM code
struct AABB
{
	Vector3 min, max; // axis aligned bounding box
	inline void fit(const Vector3 &p)
	{
		if (p.x < min.x)
			min.x = p.x;
		if (p.y < min.y)
			min.y = p.y;
		if (p.z < min.z)
			min.z = p.z;
		max.x = fmax(p.x, max.x);
		max.y = fmax(p.y, max.y);
		max.z = fmax(p.z, max.z);
	}
	inline void reset()
	{
		min = Vector3(1e20, 1e20, 1e20);
		max = Vector3(-1e20, -1e20, -1e20);
	}
};
struct HPoint
{
	Vector3 f, pos, norm, flux;
	double r2;
	unsigned int n;
	int pix;
	bool isLight;
};
class List
{
public:
	HPoint *id;
	List *next;
	List* ListAdd(HPoint *i)
	{
		List* p = new List;
		p->id = i;
		p->next = this;
		return p;
	}
};
#endif // CSE168_RAY_H_INCLUDED
